/*
 *  Created at 2011-01-09 19:36 GMT-03:00
 *  Author: Victor Williams Stafusa da Silva
 *  JSmartUnit
 */
package org.jsmartunit.test.tools;

import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.junit.Ignore;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

/**
 * @author Victor Williams Stafusa da Silva
 */
@Ignore
public class EventTracker extends RunListener {

    private final List<Event> actualEvents;

    public EventTracker() {
        this.actualEvents = new LinkedList<Event>();
    }

    @Override
    public void testRunStarted(Description description) throws Exception {
        add(Event.forType(EventType.RUN_START, description));
    }

    @Override
    public void testRunFinished(Result result) throws Exception {
        add(Event.forType(EventType.RUN_FINISHED));
    }

    @Override
    public void testStarted(Description description) throws Exception {
        add(Event.forType(EventType.START, description));
    }

    @Override
    public void testFinished(Description description) throws Exception {
        add(Event.forType(EventType.END, description));
    }

    @Override
    public void testFailure(Failure failure) throws Exception {
        add(Event.forType(EventType.FAIL, failure));
    }

    @Override
    public void testAssumptionFailure(Failure failure) {
        add(Event.forType(EventType.ASSUME_FAIL, failure));
    }

    @Override
    public void testIgnored(Description description) throws Exception {
        add(Event.forType(EventType.IGNORE, description));
    }

    public EventTracker add(String text) {
        return add(Event.forType(EventType.MESSAGE, text));
    }

    public EventTracker add(Event event) {
        actualEvents.add(event);
        return this;
    }

    public void track(Class<?> testCase) {
        try {
            Field field = testCase.getDeclaredField("events");
            field.setAccessible(true);
            field.set(null, this);
        } catch (Throwable e) {
            throw new AssertionError(e);
        }
    }

    public List<Event> getActualEvents() {
        return actualEvents;
    }

    @Override
    public String toString() {
        return actualEvents.toString();
    }

    public Map<String, Match> getMatchesTo(EventTracker expectedEvents) {
        return getMatches(expectedEvents.getActualEvents(), this.getActualEvents());
    }

    public static Map<String, Match> getMatches(List<Event> expectedEvents, List<Event> actualEvents) {
        Iterator<Event> ita = actualEvents.iterator();
        Iterator<Event> ite = expectedEvents.iterator();

        int index = 1001;
        Map<String, Match> matches = new LinkedHashMap<String, Match>();

        while (ita.hasNext() && ite.hasNext()) {
            Event actual = ita.next();
            Event expected = ite.next();
            matches.put((index + " " + expected.getDescription()).substring(1), new Match(expected, actual));
            index++;
        }

        int unexpectedCount = 1;
        while (ita.hasNext()) {
            Event actual = ita.next();
            matches.put((index + " unexpected" + unexpectedCount).substring(1), new Match(Event.getBad(), actual));
            unexpectedCount++;
            index++;
        }

        int missingCount = 1;
        while (ite.hasNext()) {
            Event expected = ite.next();
            matches.put((index + " missing" + missingCount).substring(1), new Match(expected, Event.getBad()));
            missingCount++;
            index++;
        }

        return matches;
    }
}
